package mas;
import static com.google.common.collect.Maps.newHashMap;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

import javax.annotation.Nullable;
import javax.measure.Measure;
import javax.measure.unit.SI;

import mas.Renderer.Language;

import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Monitor;

import rinde.sim.core.Simulator;
import rinde.sim.core.TickListener;
import rinde.sim.core.TimeLapse;
import rinde.sim.core.graph.Graph;
import rinde.sim.core.graph.MultiAttributeData;
import rinde.sim.core.graph.Point;
import rinde.sim.core.model.communication.CommunicationModel;
import rinde.sim.core.model.pdp.DefaultPDPModel;
import rinde.sim.core.model.pdp.PDPModel;
import rinde.sim.core.model.road.GraphRoadModel;
import rinde.sim.core.model.road.RoadModel;
import rinde.sim.event.Listener;
import rinde.sim.serializers.SelfCycleFilter;
import rinde.sim.ui.View;
import rinde.sim.ui.renderers.GraphRoadModelRenderer;
import rinde.sim.ui.renderers.RoadUserRenderer;
import rinde.sim.ui.renderers.UiSchema;

/**
 * Dynamic Pickup and Delivery Problem
 * MAS Final Project
 * @author Vincent Franken, Ivan Mendoza
 */
public class App 
{
	//SETTINGS (ignored if experimental mode is On)
	private static boolean experimentalMode = true;
	private static final int DEFAULT_NUMBER_AGENTS = 100;
	private static double DEFAULT_MASTERS_PERCENTAGE = 20/100d; 
	public static double communicationQualityPercentage = 0.5;
	public static int testingBoxes = 10;
	
	//CONSTANTS
	public static final int TIME_MULTIPLIER = 1000;
	public static int totalBoxes = 0;
	public static int totalDeliveredBoxes = 0;
	public static int[] deliveredBoxes;
	public static List<Report> reports;
	public static List<Master> masters;
	private static final String MAP_FILE = "/mas/data/leuven-simple.dot";
	//private static final String DATASET_FILE = ROOT_FOLDER.concat("data\\req_rapide_1_240_24");
	private static final java.util.Map<String, Graph<MultiAttributeData>> GRAPH_CACHE = newHashMap();

	private static final int seed = 234;
	// initialize a random generator which we use throughout this
	// 'experiment'
	private static final RandomGenerator rnd = new MersenneTwister(seed); 
	private static final RoadModel roadModel = new GraphRoadModel(loadGraph(MAP_FILE));
	// values read out of the leuven-simple.dot file, a bit easier to do it like this than by writing parse code to look for this info
	private static final double lowestx = 3206333;
	private static final double highestx = 3362372;
	private static final double lowesty = 25635076;
	private static final double highesty = 25775177;
	private static final double mapWidth = highestx - lowestx;
	private static final double mapHeight = highesty - lowesty;
	private static List boxes;
	private static Box current_box;	
	public static long pickupError = 0;
	public static long deliveryError = 0;
	public static long totalTraveledDistance = 0;
	
	/**
	 * Main invoked method
	 * @param args
	 */
	public static void main( String[] args )
	{
		final long endTime = args != null && args.length >= 1 ? Long
				.parseLong(args[0]) : 60000000;
		if (App.experimentalMode)
			App.performExperiments(endTime);
		else
			run(endTime, null /* new Display() */, null, null); //true for testing    	
	}
	
	/**
	 * Performs several experiments as described in setExperiments method
	 * @param endTime
	 */
	private static void performExperiments(long endTime){
		//initialize
		int runs = 10;		
		App.reports = new ArrayList<Report>(runs);
		List<Experiment> experiments = App.setExperiments();
		Iterator<Experiment> it = experiments.iterator();		

		//perform experiments
		while(it.hasNext()){
			Experiment e = it.next();
			App.pickupError = 0;
			App.deliveryError = 0;
			App.totalTraveledDistance = 0;
			App.masters = null;
			App.totalBoxes = 0;
			App.totalDeliveredBoxes = 0;
			App.testingBoxes = e.getNumberOfBoxes();
			App.DEFAULT_MASTERS_PERCENTAGE = e.getMasterSlaveRatio();
			App.communicationQualityPercentage = e.getCommunicationQualityPercentage();	
			System.out.println("START EXPERIMENT#"+e.getNumber());
			e.execute(endTime);
		}		
		//present results
		int i;
		for(i=0;i<App.reports.size();i++){
			App.reports.get(i).plotResults(experiments.get(i));
		}
	}

	private static List<Experiment> setExperiments() {
		List<Experiment> experiments = new ArrayList<Experiment>();
		//19 runs, with boxes blocking factor
//		experiments.add(new Experiment(1,0.01d, 0.1d, 50));
//		experiments.add(new Experiment(2,0.5d, 0.5d, 50));
//		experiments.add(new Experiment(3,1d, 1d, 50));
//		experiments.add(new Experiment(4,0.01d, 0.5d, 50));
//		experiments.add(new Experiment(5,1d, 0.1d, 50));
//		experiments.add(new Experiment(6,0.5d, 0.1d, 50));
//		experiments.add(new Experiment(7,1d, 0.5d, 50));
//		experiments.add(new Experiment(8,0.5d, 1d, 50));
//		experiments.add(new Experiment(9,0.01d, 1d, 50));
//		
//		experiments.add(new Experiment(10,0.01d, 0.1d, 150));
//		experiments.add(new Experiment(11,0.5d, 0.5d, 150));
//		experiments.add(new Experiment(12,1d, 1d, 150));
//		experiments.add(new Experiment(13,0.01d, 0.5d, 150));
//		experiments.add(new Experiment(14,1d, 0.1d, 150));
//		experiments.add(new Experiment(15,0.5d, 0.1d, 150));
//		experiments.add(new Experiment(16,1d, 0.5d, 150));
//		experiments.add(new Experiment(17,0.5d, 1d, 150));
//		experiments.add(new Experiment(18,0.01d, 1d, 150));
		
		//1 master
//		experiments.add(new Experiment(1,0.01d, 0d, 50));
//		experiments.add(new Experiment(2,0.01d, 0.25d, 50));
//		experiments.add(new Experiment(3,0.01d, 0.5d, 50));
//		experiments.add(new Experiment(4,0.01d, 0.75d, 50));
//		experiments.add(new Experiment(5,0.01d, 1d, 50));
//		experiments.add(new Experiment(6,0.01d, 0d, 150));
//		experiments.add(new Experiment(7,0.01d, 0.25d, 150));
//		experiments.add(new Experiment(8,0.01d, 0.5d, 150));
//		experiments.add(new Experiment(9,0.01d, 0.75d, 150));
//		experiments.add(new Experiment(10,0.01d, 1d, 150));		
//		//10 masters / 90 slaves
//		experiments.add(new Experiment(11,0.1d, 0d, 50));
//		experiments.add(new Experiment(12,0.1d, 0.25d, 50));
//		experiments.add(new Experiment(13,0.1d, 0.5d, 50));
//		experiments.add(new Experiment(14,0.1d, 0.75d, 50));
//		experiments.add(new Experiment(15,0.1d, 1d, 50));
//		experiments.add(new Experiment(16,0.1d, 0d, 150));
//		experiments.add(new Experiment(17,0.1d, 0.25d, 150));
//		experiments.add(new Experiment(18,0.1d, 0.5d, 150));
//		experiments.add(new Experiment(19,0.1d, 0.75d, 150));
//		experiments.add(new Experiment(20,0.1d, 1d, 150));		
//		//20 masters / 80 slaves
//		experiments.add(new Experiment(21,0.2d, 0d, 50));
//		experiments.add(new Experiment(22,0.2d, 0.25d, 50));
//		experiments.add(new Experiment(23,0.2d, 0.5d, 50));
//		experiments.add(new Experiment(24,0.2d, 0.75d, 50));
//		experiments.add(new Experiment(25,0.2d, 1d, 50));
//		experiments.add(new Experiment(26,0.2d, 0d, 150));
//		experiments.add(new Experiment(27,0.2d, 0.25d, 150));
//		experiments.add(new Experiment(28,0.2d, 0.5d, 150));
//		experiments.add(new Experiment(29,0.2d, 0.75d, 150));
//		experiments.add(new Experiment(30,0.2d, 1d, 150));		
//		//30 masters / 70 slaves
//		experiments.add(new Experiment(31,0.3d, 0d, 50));
//		experiments.add(new Experiment(32,0.3d, 0.25d, 50));
//		experiments.add(new Experiment(33,0.3d, 0.5d, 50));
//		experiments.add(new Experiment(34,0.3d, 0.75d, 50));
//		experiments.add(new Experiment(35,0.3d, 1d, 50));
//		experiments.add(new Experiment(36,0.3d, 0d, 150));
//		experiments.add(new Experiment(37,0.3d, 0.25d, 150));
//		experiments.add(new Experiment(38,0.3d, 0.5d, 150));
//		experiments.add(new Experiment(39,0.3d, 0.75d, 150));
//		experiments.add(new Experiment(40,0.3d, 1d, 150));		
//		//40 masters / 60 slaves
//		experiments.add(new Experiment(41,0.4d, 0d, 50));
//		experiments.add(new Experiment(42,0.4d, 0.25d, 50));
//		experiments.add(new Experiment(43,0.4d, 0.5d, 50));
//		experiments.add(new Experiment(44,0.4d, 0.75d, 50));
//		experiments.add(new Experiment(45,0.4d, 1d, 50));
//		experiments.add(new Experiment(46,0.4d, 0d, 150));
//		experiments.add(new Experiment(47,0.4d, 0.25d, 150));
//		experiments.add(new Experiment(48,0.4d, 0.5d, 150));
//		experiments.add(new Experiment(49,0.4d, 0.75d, 150));
//		experiments.add(new Experiment(50,0.4d, 1d, 150));
//		//50 masters / 50 slaves
//		experiments.add(new Experiment(51,0.5d, 0d, 50));
//		experiments.add(new Experiment(52,0.5d, 0.25d, 50));
//		experiments.add(new Experiment(53,0.5d, 0.5d, 50));
//		experiments.add(new Experiment(54,0.5d, 0.75d, 50));
//		experiments.add(new Experiment(55,0.5d, 1d, 50));
//		experiments.add(new Experiment(56,0.5d, 0d, 150));
//		experiments.add(new Experiment(57,0.5d, 0.25d, 150));
//		experiments.add(new Experiment(58,0.5d, 0.5d, 150));
//		experiments.add(new Experiment(59,0.5d, 0.75d, 150));
//		experiments.add(new Experiment(60,0.5d, 1d, 150));
		//60 masters / 40 slaves
				experiments.add(new Experiment(61,0.6d, 0d, 50));
				experiments.add(new Experiment(62,0.6d, 0.25d, 50));
				experiments.add(new Experiment(63,0.6d, 0.5d, 50));
				experiments.add(new Experiment(64,0.6d, 0.75d, 50));
				experiments.add(new Experiment(65,0.6d, 1d, 50));
				experiments.add(new Experiment(66,0.6d, 0d, 150));
				experiments.add(new Experiment(67,0.6d, 0.25d, 150));
				experiments.add(new Experiment(68,0.6d, 0.5d, 150));
				experiments.add(new Experiment(69,0.6d, 0.75d, 150));
				experiments.add(new Experiment(70,0.6d, 1d, 150));
				//70 masters / 30 slaves
				experiments.add(new Experiment(71,0.7d, 0d, 50));
				experiments.add(new Experiment(72,0.7d, 0.25d, 50));
				experiments.add(new Experiment(73,0.7d, 0.5d, 50));
				experiments.add(new Experiment(74,0.7d, 0.75d, 50));
				experiments.add(new Experiment(75,0.7d, 1d, 50));
				experiments.add(new Experiment(76,0.7d, 0d, 150));
				experiments.add(new Experiment(77,0.7d, 0.25d, 150));
				experiments.add(new Experiment(78,0.7d, 0.5d, 150));
				experiments.add(new Experiment(79,0.7d, 0.75d, 150));
				experiments.add(new Experiment(80,0.7d, 1d, 150));
				//80 masters / 20 slaves
				experiments.add(new Experiment(81,0.8d, 0d, 50));
				experiments.add(new Experiment(82,0.8d, 0.25d, 50));
				experiments.add(new Experiment(83,0.8d, 0.5d, 50));
				experiments.add(new Experiment(84,0.8d, 0.75d, 50));
				experiments.add(new Experiment(85,0.8d, 1d, 50));
				experiments.add(new Experiment(86,0.8d, 0d, 150));
				experiments.add(new Experiment(87,0.8d, 0.25d, 150));
				experiments.add(new Experiment(88,0.8d, 0.5d, 150));
				experiments.add(new Experiment(89,0.8d, 0.75d, 150));
				experiments.add(new Experiment(90,0.8d, 1d, 150));
				//90 masters / 10 slaves
				experiments.add(new Experiment(91,0.9d, 0d, 50));
				experiments.add(new Experiment(92,0.9d, 0.25d, 50));
				experiments.add(new Experiment(93,0.9d, 0.5d, 50));
				experiments.add(new Experiment(94,0.9d, 0.75d, 50));
				experiments.add(new Experiment(95,0.9d, 1d, 50));
				experiments.add(new Experiment(96,0.9d, 0d, 150));
				experiments.add(new Experiment(97,0.9d, 0.25d, 150));
				experiments.add(new Experiment(98,0.9d, 0.5d, 150));
				experiments.add(new Experiment(99,0.9d, 0.75d, 150));
				experiments.add(new Experiment(100,0.9d, 1d, 150));
				//100 masters
				experiments.add(new Experiment(101,1d, 0d, 50));
				experiments.add(new Experiment(102,1d, 0.25d, 50));
				experiments.add(new Experiment(103,1d, 0.5d, 50));
				experiments.add(new Experiment(104,1d, 0.75d, 50));
				experiments.add(new Experiment(105,1d, 1d, 50));
				experiments.add(new Experiment(106,1d, 0d, 150));
				experiments.add(new Experiment(107,1d, 0.25d, 150));
				experiments.add(new Experiment(108,1d, 0.5d, 150));
				experiments.add(new Experiment(109,1d, 0.75d, 150));
				experiments.add(new Experiment(110,1d, 1d, 150));
		
		
//		experiments.add(new Experiment(1,0.1d, 0.2d, 50));
//		experiments.add(new Experiment(2,0.2d, 0.2d, 50));
//		experiments.add(new Experiment(3,0.3d, 0.2d, 50));
//		experiments.add(new Experiment(4,0.4d, 0.2d, 50));
//		experiments.add(new Experiment(5,0.5d, 0.2d, 50));
//		experiments.add(new Experiment(6,0.6d, 0.2d, 50));
//		experiments.add(new Experiment(7,0.7d, 0.2d, 50));
//		experiments.add(new Experiment(8,0.8d, 0.2d, 50));
//		experiments.add(new Experiment(9,0.9d, 0.2d, 50));
//		experiments.add(new Experiment(10,1d, 0.2d, 50));
//		experiments.add(new Experiment(11,0.1d, 0.4d, 50));
//		experiments.add(new Experiment(12,0.2d, 0.4d, 50));
//		experiments.add(new Experiment(13,0.3d, 0.4d, 50));
//		experiments.add(new Experiment(14,0.4d, 0.4d, 50));
//		experiments.add(new Experiment(15,0.5d, 0.4d, 50));
//		experiments.add(new Experiment(16,0.6d, 0.4d, 50));
//		experiments.add(new Experiment(17,0.7d, 0.4d, 50));
//		experiments.add(new Experiment(18,0.8d, 0.4d, 50));
//		experiments.add(new Experiment(19,0.9d, 0.4d, 50));
//		experiments.add(new Experiment(20,1d, 0.4d, 50));
//		experiments.add(new Experiment(21,0.1d, 0.6d, 50));
//		experiments.add(new Experiment(22,0.2d, 0.6d, 50));
//		experiments.add(new Experiment(23,0.3d, 0.6d, 50));
//		experiments.add(new Experiment(24,0.4d, 0.6d, 50));
//		experiments.add(new Experiment(25,0.5d, 0.6d, 50));
//		experiments.add(new Experiment(26,0.6d, 0.6d, 50));
//		experiments.add(new Experiment(27,0.7d, 0.6d, 50));
//		experiments.add(new Experiment(28,0.8d, 0.6d, 50));
//		experiments.add(new Experiment(29,0.9d, 0.6d, 50));
//		experiments.add(new Experiment(30,1d, 0.6d, 50));
//		experiments.add(new Experiment(31,0.1d, 0.8d, 50));
//		experiments.add(new Experiment(32,0.2d, 0.8d, 50));
//		experiments.add(new Experiment(33,0.3d, 0.8d, 50));
//		experiments.add(new Experiment(34,0.4d, 0.8d, 50));
//		experiments.add(new Experiment(35,0.5d, 0.8d, 50));
//		experiments.add(new Experiment(36,0.6d, 0.8d, 50));
//		experiments.add(new Experiment(37,0.7d, 0.8d, 50));
//		experiments.add(new Experiment(38,0.8d, 0.8d, 50));
//		experiments.add(new Experiment(39,0.9d, 0.8d, 50));
//		experiments.add(new Experiment(40,1d, 0.8d, 50));
//		experiments.add(new Experiment(41,0.1d, 1d, 50));
//		experiments.add(new Experiment(42,0.2d, 1d, 50));
//		experiments.add(new Experiment(43,0.3d, 1d, 50));
//		experiments.add(new Experiment(44,0.4d, 1d, 50));
//		experiments.add(new Experiment(45,0.5d, 1d, 50));
//		experiments.add(new Experiment(46,0.6d, 1d, 50));
//		experiments.add(new Experiment(47,0.7d, 1d, 50));
//		experiments.add(new Experiment(48,0.8d, 1d, 50));
//		experiments.add(new Experiment(49,0.9d, 1d, 50));
//		experiments.add(new Experiment(50,1d, 1d, 50));
////		//Blocked factorial design (CONGESTED=150 boxes)
//		experiments.add(new Experiment(51,0.1d, 0.2d, 150));
//		experiments.add(new Experiment(52,0.2d, 0.2d, 150));
//		experiments.add(new Experiment(53,0.3d, 0.2d, 150));
//		experiments.add(new Experiment(54,0.4d, 0.2d, 150));
//		experiments.add(new Experiment(55,0.5d, 0.2d, 150));
//		experiments.add(new Experiment(56,0.6d, 0.2d, 150));
//		experiments.add(new Experiment(57,0.7d, 0.2d, 150));
//		experiments.add(new Experiment(58,0.8d, 0.2d, 150));
//		experiments.add(new Experiment(59,0.9d, 0.2d, 150));
//		experiments.add(new Experiment(60,1d, 0.2d, 150));
//		experiments.add(new Experiment(61,0.1d, 0.4d, 150));
//		experiments.add(new Experiment(62,0.2d, 0.4d, 150));
//		experiments.add(new Experiment(63,0.3d, 0.4d, 150));
//		experiments.add(new Experiment(64,0.4d, 0.4d, 150));
//		experiments.add(new Experiment(65,0.5d, 0.4d, 150));
//		experiments.add(new Experiment(66,0.6d, 0.4d, 150));
//		experiments.add(new Experiment(67,0.7d, 0.4d, 150));
//		experiments.add(new Experiment(68,0.8d, 0.4d, 150));
//		experiments.add(new Experiment(69,0.9d, 0.4d, 150));
//		experiments.add(new Experiment(70,1d, 0.4d, 150));
//		experiments.add(new Experiment(71,0.1d, 0.6d, 150));
//		experiments.add(new Experiment(72,0.2d, 0.6d, 150));
//		experiments.add(new Experiment(73,0.3d, 0.6d, 150));
//		experiments.add(new Experiment(74,0.4d, 0.6d, 150));
//		experiments.add(new Experiment(75,0.5d, 0.6d, 150));
//		experiments.add(new Experiment(76,0.6d, 0.6d, 150));
//		experiments.add(new Experiment(77,0.7d, 0.6d, 150));
//		experiments.add(new Experiment(78,0.8d, 0.6d, 150));
//		experiments.add(new Experiment(79,0.9d, 0.6d, 150));
//		experiments.add(new Experiment(80,1d, 0.6d, 150));
//		experiments.add(new Experiment(81,0.1d, 0.8d, 150));
//		experiments.add(new Experiment(82,0.2d, 0.8d, 150));
//		experiments.add(new Experiment(83,0.3d, 0.8d, 150));
//		experiments.add(new Experiment(84,0.4d, 0.8d, 150));
//		experiments.add(new Experiment(85,0.5d, 0.8d, 150));
//		experiments.add(new Experiment(86,0.6d, 0.8d, 150));
//		experiments.add(new Experiment(87,0.7d, 0.8d, 150));
//		experiments.add(new Experiment(88,0.8d, 0.8d, 150));
//		experiments.add(new Experiment(89,0.9d, 0.8d, 150));
//		experiments.add(new Experiment(90,1d, 0.8d, 150));
//		experiments.add(new Experiment(91,0.1d, 1d, 150));
//		experiments.add(new Experiment(92,0.2d, 1d, 150));
//		experiments.add(new Experiment(93,0.3d, 1d, 150));
//		experiments.add(new Experiment(94,0.4d, 1d, 150));
//		experiments.add(new Experiment(95,0.5d, 1d, 150));
//		experiments.add(new Experiment(96,0.6d, 1d, 150));
//		experiments.add(new Experiment(97,0.7d, 1d, 150));
//		experiments.add(new Experiment(98,0.8d, 1d, 150));
//		experiments.add(new Experiment(99,0.9d, 1d, 150));
//		experiments.add(new Experiment(100,1d, 1d, 150));
//		experiments.add(new Experiment(101,0.01d, 1d, 50));
//		experiments.add(new Experiment(102,0.01d, 1d, 150));
		return experiments;
	}

	/**
	 * A customer with very permissive time windows.
	 */
	//    static class Request extends Box {
	//      Request(Box aBox) {
	//        super(aBox.getArrivalTime(),aBox.getpPickup(),aBox.getPickupDuration(),aBox.getPickupTimeWindow(),aBox.getDestination(),aBox.getDeliveryDuration(),aBox.getDeliveryTimeWindow());
	//        setStartPosition(aBox.getpPickup());
	//      }
	//
	//      @Override
	//      public void initRoadPDP(RoadModel pRoadModel, PDPModel pPdpModel) {}
	//    }

	/**
	 * Run simulation
	 * @param testing
	 */
	public static Simulator run(final long endTime,@Nullable Display display, @Nullable Monitor m, @Nullable Listener list) {
		//final List<Master> masters;
		final List<Box> boxes;       
		// initialize a new Simulator instance
		final Simulator sim = new Simulator(rnd, Measure.valueOf(10000L,
				SI.MILLI(SI.SECOND)));
		final DefaultPDPModel pdpModel = new DefaultPDPModel();
		final CommunicationModel communicationModel = new CommunicationModel(rnd,false);
		sim.register(roadModel);
		sim.register(pdpModel);
		sim.register(communicationModel);
		sim.configure();
		// Build tree of agents and register with Simulator
		App.masters = App.buildTree(roadModel,pdpModel);
		Iterator<Master> it_master = App.masters.iterator();        
		while(it_master.hasNext()){
			Master master = it_master.next();
			sim.register(master);
			Iterator<Slave> it_slave = master.getSlaves().iterator();  
			master.initRoadPDP(roadModel, pdpModel);
			//register slave agents
			while(it_slave.hasNext()){
				Slave slave = it_slave.next();
				sim.register(slave);
				slave.initRoadPDP(roadModel, pdpModel);
			}
		}

		// Generate requests to start simulation
		boxes = App.buildRequests(roadModel);
		App.totalBoxes = boxes.size();
		App.deliveredBoxes = new int[App.totalBoxes];

		final Iterator<Box>it_box = boxes.iterator();
		App.current_box = it_box.next(); //first item in the list
		//Program ticks
		sim.addTickListener(new TickListener(){
			public void afterTick(TimeLapse time) {}
			public void tick(TimeLapse time) {
				int masterid;
				if ((time.getStartTime()/4 > endTime) || (App.totalDeliveredBoxes == App.totalBoxes)) {
					 //turn this sysout on/off for printing a summary of one simulation run
//					for(Box box: boxes){
//						System.out.println("Box "+ box.getBoxNumber() +" delivered: " + box.delivered);
//					}
//					System.out.println("Total pickup error:"+App.pickupError);
//					System.out.println("Total delivery error:"+App.deliveryError);
//					System.out.println("Total simulation time:"+time.getEndTime());
//					System.out.println("Total traveled distance:"+App.totalTraveledDistance);
//					System.out.println("Objective function value(sum):"+(App.pickupError+App.deliveryError+time.getEndTime()+App.totalTraveledDistance));
					sim.stop();
					if (App.experimentalMode) 
						App.reports.add(new Report(App.pickupError, App.deliveryError,App.totalTraveledDistance,time.getStartTime()));
					else{
						Report r = new Report(App.pickupError, App.deliveryError,App.totalTraveledDistance,time.getEndTime());
						r.plotResults(new Experiment(1, 0,0, 0));
						System.exit(0);
					}
					
				}else{
					if (App.current_box != null){ //if new box in list, check time and assign
						if ((App.current_box.getArrivalTime() >= time.getStartTime()) && (App.current_box.getArrivalTime() < time.getEndTime())){
							sim.register(App.current_box);
							masterid = App.assignBox(App.masters, App.current_box);    
							if (it_box.hasNext()) 
								App.current_box = it_box.next();
							else
								App.current_box = null;
						}
					}//else{
					//						sim.stop(); //If no other boxes just stop
					//						System.exit(0);
					//					}

				}
			}          
		});
		//comment this rule if you want to run it without gui (runs faster)
		if (!App.experimentalMode)
			createAndSetupGUI(sim,display,m,list,roadModel);
		sim.start();
		return sim;
	}
	
	/**
	 * Tells if a box was already delivered
	 * @param boxNumber
	 * @return
	 */
	static boolean wasThisBoxDelivered(int boxNumber){
		int i;
		if (App.deliveredBoxes.length > 0){
			for(i=0;i<App.deliveredBoxes.length;i++){
				if(App.deliveredBoxes[i] == boxNumber)
					return true;
			}
		}
		return false;
	}

	private static void createAndSetupGUI(Simulator simulator,@Nullable Display display, @Nullable Monitor m, @Nullable Listener list, RoadModel rm) {
		final UiSchema uis = new UiSchema();
		uis.add(Slave.class,  "/graphics/flat/small-truck-32.png");
		uis.add(Master.class, "/graphics/flat/truck-32.png");
		uis.add(Box.class, "/graphics/flat/order-new.png");
		final UiSchema schema2 = new UiSchema();
		schema2.add("color.black", new RGB(0, 0, 0));
		schema2.add("color.yellow", new RGB(0xff, 0, 0));
		schema2.add("color.green", new RGB(0x0, 0x80, 0));
		final View.Builder view = View.create(simulator)
				.with(new GraphRoadModelRenderer())
				.with(new RoadUserRenderer(uis, false))
				.with(new Renderer(Language.ENGLISH))
				.with(new MyMessagingLayerRenderer(rm, schema2))
				.setTitleAppendix("Master and slave trucks");

		if (m != null && list != null) {
			view.displayOnMonitor(m)
			.setSpeedUp(4)
			.setResolution(m.getClientArea().width, m.getClientArea().height)
			.setDisplay(display)
			.setCallback(list)
			.setAsync()
			.enableAutoPlay()
			.enableAutoClose();
		}
		view.show();


	}

	/**
	 * Helps assigning a box to an agent (master/slave)
	 * @param masters
	 * @param b
	 * @return
	 */
	private static int assignBox(List<Master> masters, Box b){
		Iterator<Master> it_master = masters.iterator();
		Boolean assigned = false;
		int masterid = -1;

		while(it_master.hasNext() && assigned == false){
			Master m = it_master.next();
			//System.out.println(m.getZone().getBottomLeft());
			if (m.getZone().containsPoint(b.getpPickup())){ //check pickup zone of box
				b.setPickupZoneNumber(m.getZone().getZoneNumber());
				//if(m.assign(b)){ //try to assign
				m.addToQueue(b);
				assigned = true;
				masterid = m.getAgentNumber(); 
				//}
			}
			if(m.getZone().containsPoint(b.getpDelivery())){
				b.setDeliveryZoneNumber(m.getZoneNumber());
			}
		}
		return masterid;
	}


	/**
	 * Load agents in memory
	 * @param roadModel
	 * @return
	 */
	private static List<Master> buildTree(RoadModel roadModel, PDPModel pdpModel){
		//Create agents tree with zones
		Company company = new Company();
		company.buildTree(App.DEFAULT_NUMBER_AGENTS, App.DEFAULT_MASTERS_PERCENTAGE,lowestx,lowesty,mapWidth, mapHeight, roadModel, pdpModel);
		return company.getTrucks();
	}

	private static List<Box> buildRequests(RoadModel roadModel){
		//return Box.generateBoxes(DATASET_FILE, roadModel);// App.lowestx, App.lowesty);
		return Box.generateBoxes(roadModel);
	}

	// load the graph file
	static Graph<MultiAttributeData> loadGraph(String name) {
		try {
			if (GRAPH_CACHE.containsKey(name)) {
				return GRAPH_CACHE.get(name);
			}
			final Graph<MultiAttributeData> g = MyDotGraphSerializer
					.getMultiAttributeGraphSerializer(
							new SelfCycleFilter()).read(
									App.class.getResourceAsStream(name));

			GRAPH_CACHE.put(name, g);
			return g;
		} catch (final FileNotFoundException e) {
			throw new IllegalStateException(e);
		} catch (final IOException e) {
			throw new IllegalStateException(e);
		}
	}

	public static Point getPointToGoToWhenIdle(Truck truck) {
		System.out.println("This must not be printed, it must be overloaded");
		return null;
	}	
	
	public static Point getPointToGoToWhenIdle(Master master) {
		return master.getZone().getReferencePoint();
	}	
	
	public static Point getPointToGoToWhenIdle(Slave slave) {
		return slave.getCurrentMaster().getZone().getReferencePoint();
	}

}